#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
可视化模块

为MCP决策优化服务提供直观的可视化功能，包括：
1. 优化结果可视化
2. 算法性能对比图
3. 决策变量影响分析
4. 帕累托前沿图
5. 风险分析图表
"""

import matplotlib.pyplot as plt
import matplotlib.patches as patches
import seaborn as sns
import numpy as np
import pandas as pd
from typing import Dict, List, Any, Optional, Tuple
import warnings
warnings.filterwarnings('ignore')

# 强化中文字体配置
plt.rcParams['font.sans-serif'] = ['Microsoft YaHei', 'SimHei', 'SimSun', 'DejaVu Sans', 'Arial Unicode MS']
plt.rcParams['font.family'] = 'sans-serif'
plt.rcParams['axes.unicode_minus'] = False
plt.rcParams['font.size'] = 12
plt.rcParams['axes.titlesize'] = 14
plt.rcParams['axes.labelsize'] = 12
plt.rcParams['xtick.labelsize'] = 10
plt.rcParams['ytick.labelsize'] = 10
plt.rcParams['legend.fontsize'] = 10
plt.rcParams['figure.titlesize'] = 16

# 确保中文字体正确显示
import matplotlib
matplotlib.rcParams['font.sans-serif'] = ['Microsoft YaHei', 'SimHei', 'SimSun', 'DejaVu Sans', 'Arial Unicode MS']
matplotlib.rcParams['font.family'] = 'sans-serif'
matplotlib.rcParams['axes.unicode_minus'] = False
matplotlib.rcParams['font.size'] = 12
matplotlib.rcParams['axes.titlesize'] = 14
matplotlib.rcParams['axes.labelsize'] = 12
matplotlib.rcParams['xtick.labelsize'] = 10
matplotlib.rcParams['ytick.labelsize'] = 10
matplotlib.rcParams['legend.fontsize'] = 10
matplotlib.rcParams['figure.titlesize'] = 16

class OptimizationVisualizer:
    """
    优化结果可视化器
    """
    
    def __init__(self, style: str = 'seaborn-v0_8', figsize: Tuple[int, int] = (12, 8)):
        """
        初始化可视化器
        
        Args:
            style: 图表样式
            figsize: 图表大小
        """
        self.style = style
        self.figsize = figsize
        self.colors = ['#2E86AB', '#A23B72', '#F18F01', '#C73E1D', '#6A994E']
        
        # 设置样式
        try:
            plt.style.use(style)
        except:
            plt.style.use('default')
    
    def plot_optimization_comparison(self, 
                                   comparison_results: Dict[str, Any], 
                                   save_path: Optional[str] = None) -> str:
        """
        绘制算法性能对比图
        
        Args:
            comparison_results: 算法比较结果
            save_path: 保存路径
            
        Returns:
            图片保存路径
        """
        fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2, figsize=(15, 12))
        fig.suptitle('算法性能对比分析', fontsize=16, fontweight='bold')
        
        if 'algorithm_results' not in comparison_results:
            return None
            
        results = comparison_results['algorithm_results']
        algorithms = list(results.keys())
        
        # 1. 目标值对比
        objective_values = [results[algo].get('objective_value', 0) for algo in algorithms]
        bars1 = ax1.bar(algorithms, objective_values, color=self.colors[:len(algorithms)])
        ax1.set_title('目标函数值对比', fontweight='bold')
        ax1.set_ylabel('目标值')
        ax1.tick_params(axis='x', rotation=45)
        
        # 添加数值标签
        for bar, value in zip(bars1, objective_values):
            height = bar.get_height()
            ax1.text(bar.get_x() + bar.get_width()/2., height + height*0.01,
                    f'{value:.2f}', ha='center', va='bottom')
        
        # 2. 执行时间对比
        execution_times = [results[algo].get('execution_time', 0) for algo in algorithms]
        bars2 = ax2.bar(algorithms, execution_times, color=self.colors[:len(algorithms)])
        ax2.set_title('执行时间对比', fontweight='bold')
        ax2.set_ylabel('时间 (秒)')
        ax2.tick_params(axis='x', rotation=45)
        
        # 添加数值标签
        for bar, value in zip(bars2, execution_times):
            height = bar.get_height()
            ax2.text(bar.get_x() + bar.get_width()/2., height + height*0.01,
                    f'{value:.4f}s', ha='center', va='bottom')
        
        # 3. 质量评分对比
        quality_scores = [results[algo].get('quality_score', 0) for algo in algorithms]
        bars3 = ax3.bar(algorithms, quality_scores, color=self.colors[:len(algorithms)])
        ax3.set_title('解质量评分对比', fontweight='bold')
        ax3.set_ylabel('质量评分 (0-1)')
        ax3.set_ylim(0, 1.1)
        ax3.tick_params(axis='x', rotation=45)
        
        # 添加数值标签
        for bar, value in zip(bars3, quality_scores):
            height = bar.get_height()
            ax3.text(bar.get_x() + bar.get_width()/2., height + 0.02,
                    f'{value:.3f}', ha='center', va='bottom')
        
        # 4. 综合性能雷达图
        categories = ['目标值', '执行效率', '解质量', '稳定性']
        
        # 标准化数据
        max_obj = max(objective_values) if max(objective_values) > 0 else 1
        max_time = max(execution_times) if max(execution_times) > 0 else 1
        
        angles = np.linspace(0, 2 * np.pi, len(categories), endpoint=False).tolist()
        angles += angles[:1]  # 闭合图形
        
        ax4 = plt.subplot(2, 2, 4, projection='polar')
        ax4.set_title('综合性能雷达图', fontweight='bold', pad=20)
        
        for i, algo in enumerate(algorithms):
            values = [
                objective_values[i] / max_obj,  # 标准化目标值
                1 - (execution_times[i] / max_time),  # 执行效率（时间越短越好）
                quality_scores[i],  # 解质量
                0.8 if results[algo].get('is_optimal', False) else 0.5  # 稳定性
            ]
            values += values[:1]  # 闭合图形
            
            ax4.plot(angles, values, 'o-', linewidth=2, 
                    label=algo, color=self.colors[i % len(self.colors)])
            ax4.fill(angles, values, alpha=0.25, color=self.colors[i % len(self.colors)])
        
        ax4.set_xticks(angles[:-1])
        ax4.set_xticklabels(categories)
        ax4.set_ylim(0, 1)
        ax4.legend(loc='upper right', bbox_to_anchor=(1.3, 1.0))
        
        plt.tight_layout()
        
        # 保存图片
        if save_path is None:
            save_path = 'algorithm_comparison.png'
        plt.savefig(save_path, dpi=300, bbox_inches='tight')
        plt.show()
        
        return save_path
    
    def plot_decision_variables_impact(self, 
                                     variables: List[str], 
                                     impacts: List[float],
                                     save_path: Optional[str] = None) -> str:
        """
        绘制决策变量影响分析图
        
        Args:
            variables: 决策变量名称列表
            impacts: 影响程度列表
            save_path: 保存路径
            
        Returns:
            图片保存路径
        """
        fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(15, 6))
        fig.suptitle('决策变量影响分析', fontsize=16, fontweight='bold')
        
        # 1. 影响程度条形图
        bars = ax1.barh(variables, impacts, color=self.colors[:len(variables)])
        ax1.set_title('变量影响程度排序', fontweight='bold')
        ax1.set_xlabel('影响程度')
        
        # 添加数值标签
        for bar, value in zip(bars, impacts):
            width = bar.get_width()
            ax1.text(width + width*0.01, bar.get_y() + bar.get_height()/2.,
                    f'{value:.3f}', ha='left', va='center')
        
        # 2. 影响程度饼图
        colors_pie = self.colors[:len(variables)]
        wedges, texts, autotexts = ax2.pie(impacts, labels=variables, colors=colors_pie,
                                          autopct='%1.1f%%', startangle=90)
        ax2.set_title('变量影响程度分布', fontweight='bold')
        
        # 美化饼图
        for autotext in autotexts:
            autotext.set_color('white')
            autotext.set_fontweight('bold')
        
        plt.tight_layout()
        
        # 保存图片
        if save_path is None:
            save_path = 'variables_impact.png'
        plt.savefig(save_path, dpi=300, bbox_inches='tight')
        plt.show()
        
        return save_path
    
    def plot_pareto_frontier(self, 
                           solutions: List[Dict[str, float]], 
                           objectives: List[str],
                           save_path: Optional[str] = None) -> str:
        """
        绘制帕累托前沿图
        
        Args:
            solutions: 解集合
            objectives: 目标函数名称
            save_path: 保存路径
            
        Returns:
            图片保存路径
        """
        if len(objectives) != 2:
            raise ValueError("帕累托前沿图仅支持双目标优化")
        
        fig, ax = plt.subplots(figsize=self.figsize)
        
        # 提取目标值
        obj1_values = [sol.get(objectives[0], 0) for sol in solutions]
        obj2_values = [sol.get(objectives[1], 0) for sol in solutions]
        
        # 绘制散点图
        scatter = ax.scatter(obj1_values, obj2_values, 
                           c=range(len(solutions)), 
                           cmap='viridis', 
                           s=100, 
                           alpha=0.7,
                           edgecolors='black',
                           linewidth=1)
        
        # 绘制帕累托前沿线
        if len(obj1_values) > 1:
            # 排序并连接帕累托前沿点
            pareto_points = list(zip(obj1_values, obj2_values))
            pareto_points.sort()
            
            pareto_x, pareto_y = zip(*pareto_points)
            ax.plot(pareto_x, pareto_y, 'r--', linewidth=2, alpha=0.8, label='帕累托前沿')
        
        ax.set_xlabel(objectives[0], fontsize=12)
        ax.set_ylabel(objectives[1], fontsize=12)
        ax.set_title('帕累托前沿分析', fontsize=14, fontweight='bold')
        ax.grid(True, alpha=0.3)
        ax.legend()
        
        # 添加颜色条
        cbar = plt.colorbar(scatter, ax=ax)
        cbar.set_label('解序号', rotation=270, labelpad=15)
        
        plt.tight_layout()
        
        # 保存图片
        if save_path is None:
            save_path = 'pareto_frontier.png'
        plt.savefig(save_path, dpi=300, bbox_inches='tight')
        plt.show()
        
        return save_path
    
    def plot_optimization_process(self, 
                                iteration_data: List[Dict[str, float]],
                                save_path: Optional[str] = None) -> str:
        """
        绘制优化过程收敛图
        
        Args:
            iteration_data: 迭代数据
            save_path: 保存路径
            
        Returns:
            图片保存路径
        """
        fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(12, 10))
        fig.suptitle('优化过程分析', fontsize=16, fontweight='bold')
        
        iterations = list(range(1, len(iteration_data) + 1))
        best_values = [data.get('best_value', 0) for data in iteration_data]
        avg_values = [data.get('avg_value', 0) for data in iteration_data]
        
        # 1. 收敛曲线
        ax1.plot(iterations, best_values, 'b-', linewidth=2, marker='o', 
                markersize=4, label='最优值')
        ax1.plot(iterations, avg_values, 'r--', linewidth=2, marker='s', 
                markersize=4, label='平均值')
        ax1.set_title('目标函数收敛过程', fontweight='bold')
        ax1.set_xlabel('迭代次数')
        ax1.set_ylabel('目标函数值')
        ax1.grid(True, alpha=0.3)
        ax1.legend()
        
        # 2. 改进幅度
        improvements = [0]
        for i in range(1, len(best_values)):
            improvement = abs(best_values[i] - best_values[i-1]) / abs(best_values[i-1]) * 100
            improvements.append(improvement)
        
        ax2.bar(iterations, improvements, color=self.colors[0], alpha=0.7)
        ax2.set_title('每代改进幅度', fontweight='bold')
        ax2.set_xlabel('迭代次数')
        ax2.set_ylabel('改进幅度 (%)')
        ax2.grid(True, alpha=0.3)
        
        plt.tight_layout()
        
        # 保存图片
        if save_path is None:
            save_path = 'optimization_process.png'
        plt.savefig(save_path, dpi=300, bbox_inches='tight')
        plt.show()
        
        return save_path
    
    def plot_risk_analysis(self, 
                          risk_data: Dict[str, Any],
                          save_path: Optional[str] = None) -> str:
        """
        绘制风险分析图
        
        Args:
            risk_data: 风险分析数据
            save_path: 保存路径
            
        Returns:
            图片保存路径
        """
        fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2, figsize=(15, 12))
        fig.suptitle('风险分析报告', fontsize=16, fontweight='bold')
        
        # 1. 蒙特卡洛模拟结果分布
        if 'simulation_results' in risk_data:
            results = risk_data['simulation_results']
            ax1.hist(results, bins=30, color=self.colors[0], alpha=0.7, edgecolor='black')
            ax1.axvline(np.mean(results), color='red', linestyle='--', linewidth=2, label=f'均值: {np.mean(results):.2f}')
            ax1.axvline(np.percentile(results, 5), color='orange', linestyle='--', linewidth=2, label='5%分位数')
            ax1.axvline(np.percentile(results, 95), color='orange', linestyle='--', linewidth=2, label='95%分位数')
            ax1.set_title('目标值分布 (蒙特卡洛模拟)', fontweight='bold')
            ax1.set_xlabel('目标值')
            ax1.set_ylabel('频次')
            ax1.legend()
            ax1.grid(True, alpha=0.3)
        
        # 2. 风险等级分布
        if 'risk_levels' in risk_data:
            risk_levels = risk_data['risk_levels']
            risk_names = list(risk_levels.keys())
            risk_values = list(risk_levels.values())
            
            colors_risk = ['green', 'yellow', 'orange', 'red']
            ax2.pie(risk_values, labels=risk_names, colors=colors_risk[:len(risk_names)],
                   autopct='%1.1f%%', startangle=90)
            ax2.set_title('风险等级分布', fontweight='bold')
        
        # 3. 敏感性分析
        if 'sensitivity_analysis' in risk_data:
            sensitivity = risk_data['sensitivity_analysis']
            variables = list(sensitivity.keys())
            sensitivities = list(sensitivity.values())
            
            bars = ax3.barh(variables, sensitivities, color=self.colors[:len(variables)])
            ax3.set_title('参数敏感性分析', fontweight='bold')
            ax3.set_xlabel('敏感性系数')
            
            # 添加数值标签
            for bar, value in zip(bars, sensitivities):
                width = bar.get_width()
                ax3.text(width + width*0.01, bar.get_y() + bar.get_height()/2.,
                        f'{value:.3f}', ha='left', va='center')
        
        # 4. 置信区间
        if 'confidence_intervals' in risk_data:
            intervals = risk_data['confidence_intervals']
            scenarios = list(intervals.keys())
            lower_bounds = [intervals[s]['lower'] for s in scenarios]
            upper_bounds = [intervals[s]['upper'] for s in scenarios]
            means = [intervals[s]['mean'] for s in scenarios]
            
            x_pos = np.arange(len(scenarios))
            ax4.errorbar(x_pos, means, 
                        yerr=[np.array(means) - np.array(lower_bounds), 
                              np.array(upper_bounds) - np.array(means)],
                        fmt='o', capsize=5, capthick=2, linewidth=2)
            ax4.set_xticks(x_pos)
            ax4.set_xticklabels(scenarios, rotation=45)
            ax4.set_title('置信区间分析', fontweight='bold')
            ax4.set_ylabel('目标值')
            ax4.grid(True, alpha=0.3)
        
        plt.tight_layout()
        
        # 保存图片
        if save_path is None:
            save_path = 'risk_analysis.png'
        plt.savefig(save_path, dpi=300, bbox_inches='tight')
        plt.show()
        
        return save_path
    
    def create_dashboard(self, 
                        optimization_results: Dict[str, Any],
                        save_path: Optional[str] = None) -> str:
        """
        创建综合仪表板
        
        Args:
            optimization_results: 优化结果数据
            save_path: 保存路径
            
        Returns:
            图片保存路径
        """
        fig = plt.figure(figsize=(20, 15))
        gs = fig.add_gridspec(3, 4, hspace=0.3, wspace=0.3)
        
        # 主标题
        fig.suptitle('MCP决策优化服务 - 综合分析仪表板', fontsize=20, fontweight='bold')
        
        # 1. 最优解展示 (左上角大图)
        ax1 = fig.add_subplot(gs[0, :2])
        if 'best_solution' in optimization_results:
            solution = optimization_results['best_solution']
            variables = list(solution.keys())
            values = list(solution.values())
            
            bars = ax1.bar(variables, values, color=self.colors[:len(variables)])
            ax1.set_title('最优决策方案', fontsize=14, fontweight='bold')
            ax1.set_ylabel('决策值')
            
            # 添加数值标签
            for bar, value in zip(bars, values):
                height = bar.get_height()
                ax1.text(bar.get_x() + bar.get_width()/2., height + height*0.01,
                        f'{value}', ha='center', va='bottom', fontweight='bold')
        
        # 2. 关键指标 (右上角)
        ax2 = fig.add_subplot(gs[0, 2:])
        ax2.axis('off')
        
        # 创建指标卡片
        metrics = {
            '最优目标值': optimization_results.get('best_objective_value', 'N/A'),
            '执行时间': f"{optimization_results.get('execution_time', 0):.4f}s",
            '算法': optimization_results.get('algorithm_used', 'N/A'),
            '质量评分': f"{optimization_results.get('quality_score', 0):.3f}"
        }
        
        y_positions = [0.8, 0.6, 0.4, 0.2]
        for i, (metric, value) in enumerate(metrics.items()):
            # 绘制卡片背景
            rect = patches.Rectangle((0.1, y_positions[i]-0.08), 0.8, 0.15, 
                                   linewidth=2, edgecolor=self.colors[i], 
                                   facecolor=self.colors[i], alpha=0.2)
            ax2.add_patch(rect)
            
            # 添加文本
            ax2.text(0.15, y_positions[i], metric, fontsize=12, fontweight='bold')
            ax2.text(0.85, y_positions[i], str(value), fontsize=12, 
                    ha='right', fontweight='bold')
        
        ax2.set_xlim(0, 1)
        ax2.set_ylim(0, 1)
        ax2.set_title('关键性能指标', fontsize=14, fontweight='bold')
        
        # 3. 算法比较 (中间左)
        ax3 = fig.add_subplot(gs[1, :2])
        if 'algorithm_comparison' in optimization_results:
            comparison = optimization_results['algorithm_comparison']
            algorithms = list(comparison.keys())
            scores = [comparison[algo].get('quality_score', 0) for algo in algorithms]
            
            bars = ax3.bar(algorithms, scores, color=self.colors[:len(algorithms)])
            ax3.set_title('算法质量对比', fontsize=14, fontweight='bold')
            ax3.set_ylabel('质量评分')
            ax3.set_ylim(0, 1.1)
        
        # 4. 风险分析 (中间右)
        ax4 = fig.add_subplot(gs[1, 2:])
        if 'risk_analysis' in optimization_results:
            risk_data = optimization_results['risk_analysis']
            if 'risk_levels' in risk_data:
                risk_levels = risk_data['risk_levels']
                labels = list(risk_levels.keys())
                sizes = list(risk_levels.values())
                colors_risk = ['green', 'yellow', 'orange', 'red']
                
                ax4.pie(sizes, labels=labels, colors=colors_risk[:len(labels)],
                       autopct='%1.1f%%', startangle=90)
                ax4.set_title('风险等级分布', fontsize=14, fontweight='bold')
        
        # 5. 优化过程 (底部左)
        ax5 = fig.add_subplot(gs[2, :2])
        if 'optimization_history' in optimization_results:
            history = optimization_results['optimization_history']
            iterations = list(range(1, len(history) + 1))
            values = [h.get('best_value', 0) for h in history]
            
            ax5.plot(iterations, values, 'b-', linewidth=2, marker='o')
            ax5.set_title('优化收敛过程', fontsize=14, fontweight='bold')
            ax5.set_xlabel('迭代次数')
            ax5.set_ylabel('目标值')
            ax5.grid(True, alpha=0.3)
        
        # 6. 决策建议 (底部右)
        ax6 = fig.add_subplot(gs[2, 2:])
        ax6.axis('off')
        
        recommendations = optimization_results.get('recommendations', [
            '✓ 使用混合优化策略获得最佳结果',
            '✓ 小规模问题保证全局最优',
            '✓ 解质量验证通过',
            '✓ 建议采用当前最优方案'
        ])
        
        ax6.text(0.05, 0.9, '决策建议', fontsize=14, fontweight='bold')
        for i, rec in enumerate(recommendations[:4]):
            ax6.text(0.05, 0.7 - i*0.15, rec, fontsize=11, 
                    bbox=dict(boxstyle="round,pad=0.3", facecolor=self.colors[i], alpha=0.3))
        
        ax6.set_xlim(0, 1)
        ax6.set_ylim(0, 1)
        
        # 保存图片
        if save_path is None:
            save_path = 'optimization_dashboard.png'
        plt.savefig(save_path, dpi=300, bbox_inches='tight')
        plt.show()
        
        return save_path

# 便捷函数
def create_quick_visualization(optimization_result: Dict[str, Any], 
                             visualization_type: str = 'dashboard') -> str:
    """
    快速创建可视化图表
    
    Args:
        optimization_result: 优化结果
        visualization_type: 可视化类型 ('dashboard', 'comparison', 'process', 'risk')
        
    Returns:
        图片保存路径
    """
    visualizer = OptimizationVisualizer()
    
    if visualization_type == 'dashboard':
        return visualizer.create_dashboard(optimization_result)
    elif visualization_type == 'comparison' and 'algorithm_results' in optimization_result:
        return visualizer.plot_optimization_comparison(optimization_result)
    elif visualization_type == 'process' and 'optimization_history' in optimization_result:
        return visualizer.plot_optimization_process(optimization_result['optimization_history'])
    elif visualization_type == 'risk' and 'risk_analysis' in optimization_result:
        return visualizer.plot_risk_analysis(optimization_result['risk_analysis'])
    else:
        # 默认创建仪表板
        return visualizer.create_dashboard(optimization_result)

if __name__ == "__main__":
    # 测试可视化功能
    print("可视化模块测试")
    
    # 模拟数据
    test_data = {
        'best_solution': {'x1': 0, 'x2': 1, 'y': 0, 'z': 0},
        'best_objective_value': 73.2,
        'execution_time': 0.001,
        'algorithm_used': 'exhaustive_search',
        'quality_score': 1.0,
        'algorithm_comparison': {
            'exhaustive': {'quality_score': 1.0, 'objective_value': 73.2, 'execution_time': 0.001},
            'intelligent': {'quality_score': 0.85, 'objective_value': 68.5, 'execution_time': 0.05}
        },
        'recommendations': [
            '✓ 使用穷举法获得全局最优解',
            '✓ 解质量验证通过',
            '✓ 执行效率优秀',
            '✓ 建议采用当前方案'
        ]
    }
    
    # 创建可视化
    visualizer = OptimizationVisualizer()
    dashboard_path = visualizer.create_dashboard(test_data, 'test_dashboard.png')
    print(f"仪表板已保存至: {dashboard_path}")